﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Interface;
using RayDen.Library.Entity.Renderer;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Surface;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Data;

namespace RayDen.RayEngine.Samplers
{

    public class LightSampler : ILightSampler
    {
        private RayEngineScene scene;
        private FastRandom rnd;

        public LightSampler(RayEngineScene scn)
        {
            this.scene = scn;
            this.rnd = new FastRandom();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void EvaluateShadow(ref Point point, ref Normal n, float u0, float u1, float u2, ref LightSample[] result, float ltProb = 0f)
        {

            switch (scene.LightSamplingStrategy)
            {
                case LightSamplingStrategy.UniformOneLight:
                    if (result == null)
                        result = new LightSample[scene.ShadowRaysPerSample];
                    for (int i = 0; i < scene.ShadowRaysPerSample; i++)
                    {
                        int currentLightIndex = scene.SampleLights(ltProb <= 0f ? rnd.NextFloat() : ltProb);
                        var light = scene.Lights[currentLightIndex];
                        var ls = result[i]??new LightSample();
                        ls.LightIndex = currentLightIndex;
                        light.EvaluateShadow(ref point, ref n, u0, u1, u2, ref ls);
                        //ls.Pdf *= (scene.ShadowRaysPerSample);
                        result[i] = ls;
                    }
                    break;
                case LightSamplingStrategy.UniformAllLights:
                    {
                        var sm = new List<LightSample>();

                        foreach (var light in scene.Lights)
                        {
                            var ls = new LightSample();
                            light.EvaluateShadow(ref point, ref n, u0, u1, u2, ref ls);
                            if (ls.Pdf > 0f)
                                sm.Add(ls);
                        }
                        result = sm.ToArray();
                    }
                    break;
            }
        }



        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void EvaluateShadow(BaseBxdf bsdfSample,float u0, float u1, float u2, ref LightSample[] result, float ltProb = 0f)
        {

            switch (scene.LightSamplingStrategy)
            {
                case LightSamplingStrategy.UniformOneLight:
                    if (result == null)
                        result = new LightSample[scene.ShadowRaysPerSample];
                    for (int i = 0; i < scene.ShadowRaysPerSample; i++)
                    {
                        int currentLightIndex = scene.SampleLights(ltProb <= 0f ? rnd.NextFloat() : ltProb);
                        var lt0 = scene.Lights[currentLightIndex] as INewLight;
                        var lt1 = scene.Lights[currentLightIndex] as ILight;
                        
                        var ls = result[i] ?? new LightSample();
                        ls.LightIndex = currentLightIndex;
                        if (lt0 == null)
                            lt1.EvaluateShadow(ref bsdfSample.HitPoint.HitPoint, ref bsdfSample.HitPoint.GeoNormal, u0, u1, u2, ref ls);
                        else 
                            lt0.EvaluateShadow(bsdfSample, u0, u1, u2, ref ls);
                        //ls.Pdf *= (scene.ShadowRaysPerSample);
                        result[i] = ls;
                    }
                    break;
                case LightSamplingStrategy.UniformAllLights:
                    {
                        var sm = new List<LightSample>();

                        foreach (var light in scene.Lights.Cast<INewLight>())
                        {
                            var ls = new LightSample();
                            light.EvaluateShadow(bsdfSample, u0, u1, u2, ref ls);
                            if (ls.Pdf > 0f)
                                sm.Add(ls);
                        }
                        result = sm.ToArray();
                    }
                    break;
            }
        }

        public void EvaluatePhoton(float u0, float u1, float u2, float u3, float u4,
                                   out LightSample[] result, float ltProb = 0f)
        {
            throw new NotImplementedException();
        }


        public RgbSpectrum SampleEnvironment(Vector vector)
        {

            return scene.SampleEnvironment(vector);
        }

        public float StrategyPdf
        {
            get
            {
                switch (scene.LightSamplingStrategy)
                {
                    case LightSamplingStrategy.UniformAllLights:
                        return scene.ShadowRaysPerSample / (float)scene.Lights.Length;
                    case LightSamplingStrategy.UniformOneLight:
                        return ((float)scene.Lights.Length);
                }
                return 0f;
            }
        }

        public static bool IsLight(string name)
        {
            return name.Contains("lt");
        }
    }
}